package com.zzyl.serve.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.RedisKeyConstant;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.serve.domain.Device;
import com.zzyl.serve.dto.DeviceDto;
import com.zzyl.serve.mapper.DeviceMapper;
import com.zzyl.serve.service.IDeviceService;
import com.zzyl.serve.vo.DeviceDetailVo;
import com.zzyl.serve.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;

/**
 * 设备Service业务层处理
 *
 * @author itheima
 * @date 2025-03-14
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id) {
        return getById(id);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return save(device) ? 1 : 0;
    }

    /**
     * 修改设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device) {
        return updateById(device) ? 1 : 0;
    }

    /**
     * 批量删除设备
     *
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备信息
     *
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id) {
        return removeById(id) ? 1 : 0;
    }


    @Autowired
    private IoTDAClient client;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询设备列表： 从物联网平台同步产品列表 到 redis
     */
    @Override
    public void syncProductList() {

        // 1. 从物联网平台获取产品列表： IOT调用接口
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        // 指定查询50条
        listProductsRequest.setLimit(50);

        ListProductsResponse response = client.listProducts(listProductsRequest);

        // 2. 解析结果，如果失败 抛出异常终止运行
        List<ProductSummary> productList = response.getProducts();
        if (ObjectUtil.isEmpty(productList)) {
            throw new BaseException("查询物联网平台产品列表 无产品信息");
        }

        // 3. 如果有结果就存入Redis
        redisTemplate.opsForValue().set(RedisKeyConstant.IOT_ALL_PRODUCT_LIST, productList);

    }


    /**
     * 从Redis查询产品列表:
     */
    @Override
    public List<ProductVo> allProduct() {

        // 1. 从Redis查询产品信息
        List<ProductSummary> productList = (List<ProductSummary>) redisTemplate.opsForValue().get(RedisKeyConstant.IOT_ALL_PRODUCT_LIST);

        // 2. 封装前端需要的vo
        if (ObjectUtil.isEmpty(productList)) {
            return CollectionUtil.empty(List.class);
        }

        // 注意 list拷贝选择copyToList：
        List<ProductVo> voList = BeanUtil.copyToList(productList, ProductVo.class);


        return voList;
    }


    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {

        // 1. 判断设备名称是否重复
        long count1 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(StringUtils.isNotBlank(deviceDto.getDeviceName()), Device::getDeviceName, deviceDto.getDeviceName())
        );

        if (count1 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("设备名称重复，请重新输入");
        }

        // 2. 判断设备标识码是否重复
        long count2 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(StringUtils.isNotBlank(deviceDto.getNodeId()), Device::getNodeId, deviceDto.getNodeId())
        );
        if (count2 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("设备标识码重复，请重新输入");
        }


        // 3. 判断同一位置是否已绑定相同产品
        long count3 = count(
                new LambdaQueryWrapper<Device>()
                        .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                        .eq(Device::getLocationType, deviceDto.getLocationType())
                        .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                        .eq(Device::getProductKey, deviceDto.getProductKey())
        );
        if (count3 > 0) {
            // 设备名称已存在，终止运行
            throw new BaseException("同一位置已绑定相同产品，请重新输入");
        }


        // 4. 调用IOT接口，注册设备
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();

        // 提前生成设备秘钥：
        String secret = UUID.randomUUID().toString().replace("-", "");

        // 封装调用的请求参数
        AuthInfo authInfobody = new AuthInfo();
        authInfobody.withAuthType("SECRET")
                .withSecret(secret);

        body.withDescription(deviceDto.getDeviceDescription());
        body.withAuthInfo(authInfobody);
        body.withProductId(deviceDto.getProductKey());
        body.withDeviceName(deviceDto.getDeviceName());
        body.withNodeId(deviceDto.getNodeId());

        request.withBody(body);

        // 发送请求
        AddDeviceResponse response = new AddDeviceResponse();
        try {
            response = client.addDevice(request);
        }catch (Exception e){
            // 调用失败
            throw new BaseException("调用异常，设备注册失败，请重新输入");

        }

        // 5. 本地保存到设备表
        String deviceId = response.getDeviceId();

        // 属性拷贝
        Device device = BeanUtil.toBean(deviceDto, Device.class);

        device.setIotId(deviceId);
        device.setSecret(secret);

        // 保存：
        save(device);


    }


    /**
     * 设备详情查询:
     *   需要将数据库数据 + IOT平台数据封装VO返回
     *
     * @param iotId
     * @return
     */
    @Override
    public DeviceDetailVo findDeviceDetailById(String iotId) {

        // 1. 查询数据库数据
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<Device>()
                .eq(Device::getIotId, iotId);
        Device device = getOne(queryWrapper);


        // 2. 查询IOT平台数据
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(iotId);
        try {
            ShowDeviceResponse response = client.showDevice(request);


            // 3. 封装VO返回
            DeviceDetailVo detailVo = BeanUtil.toBean(device, DeviceDetailVo.class);

            detailVo.setDeviceStatus(response.getStatus());

            // 封装日期时间：需要格式转换  它的默认格式是 yyyy-MM-dd'T'HH:mm:ss.SSS'Z'  需要转换成我们需要的localdateTime

            if (StrUtil.isNotBlank(response.getActiveTime())){
                // 如果没有计划，这个时间是空，就需要返回空
                LocalDateTime activeTime = LocalDateTimeUtil.parse(response.getActiveTime(), DatePattern.UTC_MS_PATTERN);
                //日期时区转换
                activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                        .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                        .toLocalDateTime();
                detailVo.setActiveTime(activeTime);
            }


            // 返回vo
            return detailVo;


        }catch (Exception e){

            throw new BaseException("调用IOT平台查询详情失败，请重试~~");

        }
    }


    /**
     * 查看设备最近上报数据
     * @param iotId
     * @return
     */
    @Override
    public List<Map> queryServiceProperties(String iotId) {

        // 1. 查询物联网平台
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();

        request.withDeviceId(iotId);

        try {

            // 调用查询
            ShowDeviceShadowResponse response = client.showDeviceShadow(request);

            // 2. 解析响应数据
            DeviceShadowData shadowData = response.getShadow().get(0);
            // 将我们需要的数据转为json对象: 官方约定getProperties是json格式数据
            JSONObject jsonObject = JSONUtil.parseObj(shadowData.getReported().getProperties());

            //处理上报时间日期
            LocalDateTime activeTime =  LocalDateTimeUtil.parse(
                    shadowData.getReported().getEventTime(),
                    "yyyyMMdd'T'HHmmss'Z'"
            );
            //日期时区转换
            LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();


            // 3. 封装前端需要的数据进行返回

            List<Map> list = new ArrayList<>();

            jsonObject.forEach((k,v)->{
                Map<String,Object> map = new HashMap<>();
                map.put("functionId",k);
                map.put("value",v);
                map.put("eventTime",eventTime);
                list.add(map);
            });


            return list;

        } catch (Exception e) {

            throw new BaseException("调用IOT平台查询详情失败，请重试~~");

        }
    }

    /**
     * 查询产品详情(报警规则模块使用) -- 需要调用IOT平台接口
     * @param productKey
     * @return
     */
    @Override
    public AjaxResult queryProduct(String productKey) {

        // 注意判断数据是否为空
        if (StrUtil.isBlank(productKey)){
            return AjaxResult.error("产品KEY不能为空");
        }

        // 1. 需要调用IOT平台接口查询产品详情
        ShowProductRequest request = new ShowProductRequest();
        request.withProductId(productKey);
        try {
            ShowProductResponse response = client.showProduct(request);

            // 2. 解析响应数据
            List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();

            if (CollUtil.isNotEmpty(serviceCapabilities)){
                // 3. 封装返回
                return AjaxResult.success(serviceCapabilities);

            }

        } catch (Exception e) {
            throw new BaseException("调用IOT平台查询详情失败，请重试~~");
        }

        return AjaxResult.success(Collections.emptyList());
    }


}
